package com.rj.framework.component.tech.captcha;

import com.rj.framework.component.TechnicalComponent;
import com.rj.framework.context.Context;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * 验证码服务组件 - 支持图形、短信、邮件验证码
 */
public class CaptchaServiceComponent implements TechnicalComponent<CaptchaRequest, CaptchaResponse, Context<?>> {
    // 存储策略接口
    private final CaptchaStore store;
    // 随机数生成器
    private final SecureRandom random = new SecureRandom();
    // 配置
    private final CaptchaConfig config;
    // 短信发送器
    private final SmsSender smsSender;
    // 邮件发送器
    private final EmailSender emailSender;
    // 验证码生成器
    private final Map<CaptchaType, Supplier<String>> codeGenerators = new EnumMap<>(CaptchaType.class);
    // 清理线程
    private final ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor();

    public CaptchaServiceComponent(CaptchaConfig config,
                                   CaptchaStore store,
                                   SmsSender smsSender,
                                   EmailSender emailSender) {
        this.config = config != null ? config : new CaptchaConfig();
        this.store = store != null ? store : new MemoryCaptchaStore();
        this.smsSender = smsSender;
        this.emailSender = emailSender;
        // 初始化验证码生成器
        initializeCodeGenerators();
        // 启动过期验证码清理任务
        startCleanupTask();
    }

    @Override
    public CaptchaResponse execute(CaptchaRequest request, Context<?> context) {
        try {
            // 1. 验证请求参数
            validateRequest(request);
            // 2. 生成验证码
            String code = generateCode(request.getType());
            // 3. 创建验证码记录
            CaptchaRecord record = createRecord(request, code);
            // 4. 存储验证码
            store.store(record);
            // 5. 发送验证码
            sendCaptcha(request, code);
            // 6. 返回响应
            CaptchaResponse response = new CaptchaResponse(
                    record.getId(),
                    record.getExpireTime(),
                    record.getType(),
                    record.getType().equals(CaptchaType.IMAGE) ? imageToBase64(generateImageCaptcha(code)) : null
            );

            return response;
        } catch (CaptchaException e) {
            throw new CaptchaException(e.getErrorCode(), e.getMessage());
        }
    }

    // ================== 核心业务方法 ==================

    /**
     * 验证请求参数
     */
    private void validateRequest(CaptchaRequest request) throws CaptchaException {
        if (request == null) {
            throw new CaptchaException("INVALID_REQUEST", "请求不能为空");
        }

        if (request.getType() == null) {
            throw new CaptchaException("INVALID_TYPE", "验证码类型不能为空");
        }

        // 验证目标地址
        if (request.getTarget() == null || request.getTarget().isEmpty()) {
            throw new CaptchaException("INVALID_TARGET", "目标地址不能为空");
        }

        // 验证频率限制
        if (isRateLimited(request.getTarget(), request.getType())) {
            throw new CaptchaException("RATE_LIMITED", "验证码发送过于频繁");
        }
    }

    /**
     * 生成验证码
     */
    private String generateCode(CaptchaType type) {
        return codeGenerators.get(type).get();
    }

    /**
     * 创建验证码记录
     */
    private CaptchaRecord createRecord(CaptchaRequest request, String code) {
        int expireMinutes = request.getExpireMinutes() > 0 ?
                request.getExpireMinutes() : config.getDefaultExpireMinutes();

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expireTime = now.plusMinutes(expireMinutes);

        return new CaptchaRecord(
                UUID.randomUUID().toString(),
                code,
                request.getType(),
                request.getTarget(),
                now,
                expireTime,
                request.getClientInfo()
        );
    }

    /**
     * 发送验证码
     */
    private void sendCaptcha(CaptchaRequest request, String code) throws CaptchaException {
        switch (request.getType()) {
            case IMAGE:
                // 图形验证码在响应中返回，不需要额外发送
                break;
            case SMS:
                sendSmsCaptcha(request.getTarget(), code);
                break;
            case EMAIL:
                sendEmailCaptcha(request.getTarget(), code);
                break;
            default:
                throw new CaptchaException("UNSUPPORTED_TYPE", "不支持的验证码类型");
        }
    }

    /**
     * 发送短信验证码
     */
    private void sendSmsCaptcha(String phone, String code) throws CaptchaException {
        if (smsSender == null) {
            throw new CaptchaException("SMS_SENDER_NOT_CONFIGURED", "短信发送器未配置");
        }

        String template = config.getSmsTemplate();
        String content = template.replace("{code}", code);

        try {
            smsSender.send(phone, content);
        } catch (Exception e) {
            throw new CaptchaException("SMS_SEND_FAILED", "短信发送失败: " + e.getMessage());
        }
    }

    /**
     * 发送邮件验证码
     */
    private void sendEmailCaptcha(String email, String code) throws CaptchaException {
        if (emailSender == null) {
            throw new CaptchaException("EMAIL_SENDER_NOT_CONFIGURED", "邮件发送器未配置");
        }

        String subject = config.getEmailSubject();
        String template = config.getEmailTemplate();
        String content = template.replace("{code}", code);

        try {
            emailSender.send(email, subject, content);
        } catch (Exception e) {
            throw new CaptchaException("EMAIL_SEND_FAILED", "邮件发送失败: " + e.getMessage());
        }
    }

    /**
     * 生成图形验证码
     */
    public BufferedImage generateImageCaptcha(String code) {
        int width = config.getImageWidth();
        int height = config.getImageHeight();
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        // 设置背景
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        // 添加干扰线
        drawInterferenceLines(g, width, height);
        // 绘制验证码
        drawCode(g, code, width, height);
        g.dispose();
        return image;
    }

    /**
     * 验证验证码
     */
    public boolean verifyCaptcha(String id, String code) {
        CaptchaRecord record = store.get(id);

        if (record == null) {
            return false; // 验证码不存在
        }

        if (record.isExpired()) {
            store.remove(id); // 移除过期验证码
            return false;
        }

        if (record.getCode().equalsIgnoreCase(code)) {
            store.remove(id); // 验证成功后移除
            return true;
        }
        // 记录错误尝试
        record.incrementAttempts();
        store.update(record);
        return false;
    }

    // ================== 辅助方法 ==================

    /**
     * 初始化验证码生成器
     */
    private void initializeCodeGenerators() {
        // 数字验证码
        codeGenerators.put(CaptchaType.SMS, () -> generateNumericCode(config.getSmsCodeLength()));
        codeGenerators.put(CaptchaType.EMAIL, () -> generateNumericCode(config.getEmailCodeLength()));
        // 图形验证码（字母+数字）
        codeGenerators.put(CaptchaType.IMAGE, () -> generateAlphanumericCode(config.getImageCodeLength()));
    }

    /**
     * 启动清理任务
     */
    private void startCleanupTask() {
        cleaner.scheduleAtFixedRate(store::cleanExpired,
                1,  // 初始延迟1分钟
                5,  // 每5分钟执行一次
                TimeUnit.MINUTES
        );
    }

    /**
     * 检查是否超过频率限制
     */
    private boolean isRateLimited(String target, CaptchaType type) {
        int maxRequests = config.getRateLimit().getOrDefault(type, 5);
        Duration period = config.getRateLimitPeriod().getOrDefault(type, Duration.ofMinutes(1));
        return store.getRecentCount(target, type, period) >= maxRequests;
    }

    /**
     * 生成数字验证码
     */
    private String generateNumericCode(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    /**
     * 生成字母数字验证码
     */
    private String generateAlphanumericCode(int length) {
        String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // 避免混淆字符
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    /**
     * 绘制干扰线
     */
    private void drawInterferenceLines(Graphics2D g, int width, int height) {
        g.setColor(getRandomLightColor());
        for (int i = 0; i < 5; i++) {
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            g.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 绘制验证码
     */
    private void drawCode(Graphics2D g, String code, int width, int height) {
        Font font = new Font("Arial", Font.BOLD, config.getImageFontSize());
        g.setFont(font);

        // 字符间距
        int charSpacing = width / code.length();

        for (int i = 0; i < code.length(); i++) {
            // 随机颜色
            g.setColor(getRandomDarkColor());

            // 随机旋转角度
            double rotation = (random.nextDouble() - 0.5) * Math.PI / 8;
            g.rotate(rotation, i * charSpacing + charSpacing / 2, height / 2);

            // 绘制字符
            String ch = String.valueOf(code.charAt(i));
            g.drawString(ch, i * charSpacing + 5, height - 10);

            // 重置旋转
            g.rotate(-rotation, i * charSpacing + charSpacing / 2, height / 2);
        }
    }

    /**
     * 获取随机深色
     */
    private Color getRandomDarkColor() {
        return new Color(
                random.nextInt(100),
                random.nextInt(100),
                random.nextInt(100)
        );
    }

    /**
     * 获取随机浅色
     */
    private Color getRandomLightColor() {
        return new Color(
                200 + random.nextInt(55),
                200 + random.nextInt(55),
                200 + random.nextInt(55)
        );
    }


    /**
     * 将BufferedImage转换为Base64
     */
    public static String imageToBase64(BufferedImage image) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            ImageIO.write(image, "png", baos);
            byte[] bytes = baos.toByteArray();
            return Base64.getEncoder().encodeToString(bytes);
        } catch (IOException e) {
            throw new RuntimeException("图片转换失败", e);
        }
    }
}